package com.haiyou.mini.programs.service.impl;


import com.alibaba.fastjson.JSON;
import com.haiyou.common.exception.BusinessException;
import com.haiyou.common.util.HttpClientUtil;
import com.haiyou.common.util.RedisUtil;
import com.haiyou.common.util.StringUtils;
import com.haiyou.mini.programs.config.WxMiniConfig;
import com.haiyou.mini.programs.model.WxSessionEntity;
import com.haiyou.mini.programs.service.WxMiniAppService;
import com.haiyou.mini.programs.wxpay.WxConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.codehaus.xfire.util.Base64;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.InputStream;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 小程序服务实现
 */
@Slf4j
@RequiredArgsConstructor
public class WxMiniAppServiceImpl implements WxMiniAppService {

    private final WxMiniConfig wxPayConfig;
    private final RedisUtil redisUtil;

    public WxSessionEntity wxLogin(String encryptedData, String code, String iv) {
        Map<String, String> param = new HashMap<>(4);
        param.put("appid", wxPayConfig.getAppID());
        param.put("secret", wxPayConfig.getSecret());
        param.put("js_code", code);
        param.put("grant_type", "authorization_code");
        log.info("小程序参数=>{}", JSON.toJSONString(param));
        String wxResult = HttpClientUtil.doGet(WxConstant.CODE_SESSION_URL, param);
        log.info("wxResult=======>{}", wxResult);
        WxSessionEntity wxSessionEntity = JSON.parseObject(wxResult, WxSessionEntity.class);
        log.info("wxSessionEntity====>{}", wxSessionEntity);
        if (wxSessionEntity == null || StringUtils.isEmpty(wxSessionEntity.getSession_key())) {
            throw new RuntimeException("小程序获取失败");
        }
        String sessionKey = wxSessionEntity.getSession_key();
        String result = getPhone(encryptedData, sessionKey, iv);
        Map<String, Object> map = (Map) JSON.parse(result);
        wxSessionEntity.setPhone((String) map.get("phoneNumber"));
        return wxSessionEntity;
    }

    @Override
    public InputStream createQRCode(Map<String, Object> param) {
        WxSessionEntity wxSessionEntity = getAccessToken();
        log.info("wxSessionEntity=--------->{}", wxSessionEntity);
        log.info("小程序参数={}---->", param);

        RestTemplate rest = new RestTemplate();
        try {
            String url = WxConstant.WX_GETWXACODEUNLIMIT_URL + wxSessionEntity.getAccess_token();
            ResponseEntity<Resource> entity = rest.postForEntity(url, param, Resource.class);
            log.info("contentLength=={}", entity.getBody().contentLength());
            //小于1024字节，表示图片二维码获取失败
            if (entity.getBody().contentLength() < 1024) {
                log.error("------->查看小程序路径是否存在：{}", param);
                throw new BusinessException("生成二维码失败");
            }
            if (entity.getStatusCode().equals(HttpStatus.OK)) {
                return entity.getBody().getInputStream();
            }
        } catch (Exception e) {
            log.error("调用异常----->{}", e.getMessage());
        }
        throw new BusinessException("二维码生成失败");
    }

    @Override
    public WxSessionEntity getAccessToken() {
        String access_token = redisUtil.get(WxConstant.WX_ACCESS_TOKEN);
        if (com.haiyou.common.util.StringUtils.isNotEmpty(access_token)) {
            return JSON.parseObject(access_token, WxSessionEntity.class);
        }
        Map<String, String> param = new HashMap<>(3);
        param.put("appid", wxPayConfig.getAppID());
        param.put("secret", wxPayConfig.getSecret());
        param.put("grant_type", "client_credential");
        String wxResult = HttpClientUtil.doGet(WxConstant.ACCESS_TOKEN_URL, param);
        WxSessionEntity wxSessionEntity = JSON.parseObject(wxResult, WxSessionEntity.class);
        if (wxSessionEntity != null && StringUtils.isNotEmpty(wxSessionEntity.getAccess_token())) {
            redisUtil.set(WxConstant.WX_ACCESS_TOKEN, wxResult, WxConstant.WX_ACCESS_TOKEN_TIMEOUT, TimeUnit.SECONDS);
        }
        return wxSessionEntity;
    }

    /**
     * 解密用户敏感数据获取用户信息，获取手机号
     *
     * @param encryptedData
     * @param sessionKey
     * @param iv
     * @return
     */
    private String getPhone(String encryptedData, String sessionKey, String iv) {
        String result;
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足. 这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base
                        + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                result = new String(resultByte, "UTF-8");
                log.info(" >>>>>> 解密用户敏感数据获取用户信息返回结果：" + result + " <<<<<< ");
                return result;
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        throw new RuntimeException("解密用户错误");
    }

}
